"""
用户偏好服务
实现用户偏好学习和个性化推荐功能
"""

from typing import Dict, Any, List, Optional
import json
import logging
from datetime import datetime, timedelta
from collections import defaultdict
import numpy as np
from sqlalchemy.orm import Session
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, JSON
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import UUID

from src.config.database import Base, get_db
from src.models.user import User
from src.utils.logging import get_logger

logger = get_logger(__name__)

class UserPreferenceRecord(Base):
    """用户偏好记录模型（用于存储键值对形式的用户偏好）"""
    __tablename__ = 'user_preference_records'
    
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, index=True)
    preference_key = Column(String(100), index=True)
    preference_value = Column(JSON)  # 存储偏好值（可以是任何JSON可序列化对象）
    weight = Column(Float, default=1.0)  # 偏好权重
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)


class UserInteractionRecord(Base):
    """用户交互记录模型"""
    __tablename__ = 'user_interaction_records'
    
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, index=True)
    interaction_type = Column(String(50))  # 交互类型：搜索、点击、评分等
    content_id = Column(String(100))  # 内容ID
    content_type = Column(String(50))  # 内容类型：问题、答案、文档等
    interaction_metadata = Column('metadata', JSON)  # 交互元数据（避免使用保留字'metadata'）
    score = Column(Float, default=0.0)  # 交互评分（如点击为1.0，忽略为0.0等）
    created_at = Column(DateTime, default=datetime.utcnow)


class ContextRecord(Base):
    """上下文记录模型"""
    __tablename__ = 'context_records'
    
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(UUID(as_uuid=True), index=True)
    session_id = Column(String(100), index=True)
    context_data = Column(JSON)  # 上下文数据
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)


class UserPreferenceService:
    """用户偏好服务类"""
    
    def __init__(self):
        self.db_gen = get_db()
        self.db = next(self.db_gen)
        
        # 初始化偏好权重衰减因子
        self.decay_factor = 0.95  # 每天衰减5%
        
        # 定义不同交互类型的权重
        self.interaction_weights = {
            'search': 1.0,
            'click': 1.5,
            'like': 2.0,
            'dislike': -1.0,
            'share': 2.5,
            'save': 2.0,
            'skip': -0.5
        }
    
    def record_interaction(self, user_id: int, interaction_type: str, 
                          content_id: str, content_type: str, 
                          metadata: Optional[Dict] = None, score: float = 0.0) -> bool:
        """
        记录用户交互行为
        
        Args:
            user_id: 用户ID
            interaction_type: 交互类型
            content_id: 内容ID
            content_type: 内容类型
            metadata: 交互元数据
            score: 交互评分
            
        Returns:
            bool: 是否记录成功
        """
        try:
            interaction = UserInteractionRecord(
                user_id=user_id,
                interaction_type=interaction_type,
                content_id=content_id,
                content_type=content_type,
                interaction_metadata=metadata or {},
                score=score
            )
            
            self.db.add(interaction)
            self.db.commit()
            logger.debug(f"记录用户交互: user_id={user_id}, type={interaction_type}, content_id={content_id}")
            return True
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"记录用户交互失败: {e}")
            return False
    
    def update_preference(self, user_id: int, preference_key: str, 
                         preference_value: Any, weight: float = 1.0) -> bool:
        """
        更新用户偏好
        
        Args:
            user_id: 用户ID
            preference_key: 偏好键
            preference_value: 偏好值
            weight: 偏好权重
            
        Returns:
            bool: 是否更新成功
        """
        try:
            # 查找现有偏好
            preference = self.db.query(UserPreferenceRecord).filter(
                UserPreferenceRecord.user_id == user_id,
                UserPreferenceRecord.preference_key == preference_key
            ).first()

            if preference:
                # 更新现有偏好
                preference.preference_value = preference_value
                setattr(preference, 'weight', weight)
                setattr(preference, 'updated_at', datetime.utcnow())
            else:
                # 创建新偏好
                preference = UserPreferenceRecord(
                    user_id=user_id,
                    preference_key=preference_key,
                    preference_value=preference_value,
                    weight=weight
                )
                self.db.add(preference)
            
            self.db.commit()
            logger.debug(f"更新用户偏好: user_id={user_id}, key={preference_key}")
            return True
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"更新用户偏好失败: {e}")
            return False
    
    def get_user_preferences(self, user_id: int) -> Dict[str, Any]:
        """
        获取用户偏好
        
        Args:
            user_id: 用户ID
            
        Returns:
            Dict[str, Any]: 用户偏好字典
        """
        try:
            preferences = self.db.query(UserPreferenceRecord).filter(
                UserPreferenceRecord.user_id == user_id
            ).all()
            
            result = {}
            for pref in preferences:
                # 应用时间衰减
                days_since_update = (datetime.utcnow() - pref.updated_at).days
                decayed_weight = pref.weight * (self.decay_factor ** days_since_update)
                
                result[pref.preference_key] = {
                    'value': pref.preference_value,
                    'weight': decayed_weight
                }
            
            return result
            
        except Exception as e:
            logger.error(f"获取用户偏好失败: {e}")
            return {}
    
    def learn_preferences_from_interactions(self, user_id: int) -> bool:
        """
        从用户交互中学习偏好
        
        Args:
            user_id: 用户ID
            
        Returns:
            bool: 是否学习成功
        """
        try:
            # 获取最近30天的交互记录
            thirty_days_ago = datetime.utcnow() - timedelta(days=30)
            interactions = self.db.query(UserInteractionRecord).filter(
                UserInteractionRecord.user_id == user_id,
                UserInteractionRecord.created_at >= thirty_days_ago
            ).all()
            
            if not interactions:
                return True
            
            # 分析交互数据，提取偏好
            topic_preferences = defaultdict(float)
            content_type_preferences = defaultdict(float)
            
            # 统计各类交互
            for interaction in interactions:
                # 修复类型错误：使用getattr获取实际值而不是Column对象
                interaction_type = getattr(interaction, 'interaction_type')
                content_type = getattr(interaction, 'content_type')
                interaction_metadata = getattr(interaction, 'interaction_metadata')
                score = getattr(interaction, 'score')
                
                weight = self.interaction_weights.get(interaction_type, 1.0)
                
                # 综合权重
                combined_weight = weight * score
                
                # 从元数据中提取主题信息
                if interaction_metadata and 'topics' in interaction_metadata:
                    topics = interaction_metadata['topics']
                    if isinstance(topics, list):
                        for topic in topics:
                            topic_preferences[topic] += combined_weight
                    elif isinstance(topics, str):
                        topic_preferences[topics] += combined_weight
                
                # 记录内容类型偏好
                content_type_preferences[content_type] += combined_weight
            
            # 更新用户偏好
            for topic, weight in topic_preferences.items():
                if weight > 0:  # 只记录正向偏好
                    self.update_preference(user_id, f"topic_{topic}", topic, weight)
            
            for content_type, weight in content_type_preferences.items():
                if weight > 0:  # 只记录正向偏好
                    self.update_preference(user_id, f"content_type_{content_type}", content_type, weight)
            
            logger.info(f"从交互中学习用户偏好: user_id={user_id}")
            return True
            
        except Exception as e:
            logger.error(f"从交互中学习偏好失败: {e}")
            return False
    
    def get_personalized_recommendations(self, user_id: int, 
                                       content_pool: List[Dict], 
                                       limit: int = 10,
                                       context: Optional[Dict] = None) -> List[Dict]:
        """
        获取个性化推荐
        
        Args:
            user_id: 用户ID
            content_pool: 内容池
            limit: 推荐数量限制
            context: 上下文信息（可选）
            
        Returns:
            List[Dict]: 推荐内容列表
        """
        try:
            # 获取用户偏好
            preferences = self.get_user_preferences(user_id)
            
            # 获取用户画像
            user_profile = self.build_user_profile(user_id)
            
            if not preferences and not user_profile:
                # 如果没有偏好和画像，随机返回内容
                return content_pool[:limit]
            
            # 计算内容与偏好的匹配度
            scored_content = []
            for content in content_pool:
                score = self._calculate_content_score(content, preferences, user_profile, context)
                scored_content.append((score, content))
            
            # 按分数排序并返回前N个
            scored_content.sort(key=lambda x: x[0], reverse=True)
            return [content for _, content in scored_content[:limit]]
            
        except Exception as e:
            logger.error(f"获取个性化推荐失败: {e}")
            return content_pool[:limit]
    
    def _calculate_content_score(self, content: Dict, preferences: Dict, 
                               user_profile: Optional[Dict] = None, context: Optional[Dict] = None) -> float:
        """
        计算内容与用户偏好的匹配度分数
        
        Args:
            content: 内容数据
            preferences: 用户偏好
            user_profile: 用户画像
            context: 上下文信息
            
        Returns:
            float: 匹配度分数
        """
        score = 0.0
        
        # 如果context为None，初始化为空字典以避免后续处理出错
        if context is None:
            context = {}
        
        # 检查主题匹配
        content_topics = content.get('topics', [])
        if isinstance(content_topics, str):
            content_topics = [content_topics]
        
        for topic in content_topics:
            topic_pref = preferences.get(f"topic_{topic}")
            if topic_pref:
                score += topic_pref['weight']
        
        # 检查内容类型匹配
        content_type = content.get('type')
        if content_type:
            type_pref = preferences.get(f"content_type_{content_type}")
            if type_pref:
                score += type_pref['weight']
        
        # 添加新的匹配规则
        # 基于内容长度的偏好（有些用户喜欢详细内容，有些喜欢简洁内容）
        content_length = len(content.get('text', ''))
        length_pref = preferences.get('content_length_preference')
        if length_pref:
            preferred_length = length_pref['value']
            # 根据偏好的长度类型计算匹配度
            if preferred_length == 'short' and content_length < 200:
                score += 0.5
            elif preferred_length == 'medium' and 200 <= content_length < 500:
                score += 0.5
            elif preferred_length == 'long' and content_length >= 500:
                score += 0.5
        
        # 基于内容新鲜度的偏好
        content_date = content.get('date')
        if content_date:
            try:
                from datetime import datetime
                if isinstance(content_date, str):
                    content_datetime = datetime.fromisoformat(content_date.replace('Z', '+00:00'))
                else:
                    content_datetime = content_date
                days_old = (datetime.utcnow() - content_datetime).days
                freshness_pref = preferences.get('freshness_preference')
                if freshness_pref and freshness_pref['value'] == 'fresh':
                    # 更新鲜的内容得分更高
                    freshness_score = max(0, 1.0 - (days_old / 30.0))  # 30天内为正分
                    score += freshness_score
            except Exception:
                pass  # 如果日期解析失败，跳过新鲜度计算
        
        # 基于用户画像的匹配
        if user_profile:
            # 根据用户活跃时间推荐
            active_hours = user_profile.get('active_hours', {})
            from datetime import datetime
            current_hour = datetime.now().hour
            if current_hour in active_hours:
                score += active_hours[current_hour] * 0.1
            
            # 根据用户偏好的内容类型推荐
            preferred_content_types = user_profile.get('preferred_content_types', [])
            if content_type in preferred_content_types:
                score += 1.0
        
        # 基于上下文的匹配
        if context:
            # 上下文时间匹配
            context_time = context.get('time')
            if context_time:
                context_hour = context_time.hour
                active_hours = user_profile.get('active_hours', {}) if user_profile else {}
                if context_hour in active_hours:
                    score += active_hours[context_hour] * 0.2
            
            # 上下文场景匹配
            context_scenario = context.get('scenario')
            if context_scenario:
                scenario_pref = preferences.get(f"scenario_{context_scenario}")
                if scenario_pref:
                    score += scenario_pref['weight'] * 1.5
        
        return score

    def get_context_aware_recommendations(self, user_id: int, 
                                        content_pool: List[Dict], 
                                        context: Dict[str, Any],
                                        limit: int = 10) -> List[Dict]:
        """
        获取上下文感知的推荐
        
        Args:
            user_id: 用户ID
            content_pool: 内容池
            context: 当前上下文信息
            limit: 推荐数量限制
            
        Returns:
            List[Dict]: 推荐内容列表
        """
        try:
            # 获取用户偏好
            preferences = self.get_user_preferences(user_id)
            
            # 获取用户画像
            user_profile = self.build_user_profile(user_id)
            
            # 计算内容与上下文的匹配度
            scored_content = []
            for content in content_pool:
                score = self._calculate_context_aware_score(content, preferences, user_profile, context)
                scored_content.append((score, content))
            
            # 按分数排序并返回前N个
            scored_content.sort(key=lambda x: x[0], reverse=True)
            return [content for _, content in scored_content[:limit]]
            
        except Exception as e:
            logger.error(f"获取上下文感知推荐失败: {e}")
            return content_pool[:limit]
    
    def _calculate_context_aware_score(self, content: Dict, preferences: Dict, 
                                     user_profile: Dict, context: Dict) -> float:
        """
        计算内容与上下文的匹配度分数
        
        Args:
            content: 内容数据
            preferences: 用户偏好
            user_profile: 用户画像
            context: 当前上下文
            
        Returns:
            float: 匹配度分数
        """
        # 使用已有的计算方法作为基础
        base_score = self._calculate_content_score(content, preferences, user_profile, context)
        
        # 添加上下文相关分数
        context_score = 0.0
        
        # 时间上下文匹配
        current_time = context.get('current_time')
        if current_time and user_profile:
            # 根据用户活跃时间调整分数
            active_hours = user_profile.get('active_hours', {})
            current_hour = current_time.hour
            if current_hour in active_hours:
                context_score += active_hours[current_hour] * 0.2
        
        # 场景上下文匹配
        current_scenario = context.get('scenario')
        if current_scenario:
            scenario_pref = preferences.get(f"scenario_{current_scenario}")
            if scenario_pref:
                context_score += scenario_pref['weight'] * 1.5
        
        # 任务上下文匹配
        current_task = context.get('task')
        content_task_types = content.get('task_types', [])
        if current_task and current_task in content_task_types:
            context_score += 2.0
        
        # 设备上下文匹配
        device_type = context.get('device_type')
        content_device_support = content.get('device_support', [])
        if device_type and device_type in content_device_support:
            context_score += 1.0
        
        return base_score + context_score
    
    def analyze_user_behavior_patterns(self, user_id: int) -> Dict[str, Any]:
        """
        分析用户行为模式
        
        Args:
            user_id: 用户ID
            
        Returns:
            Dict[str, Any]: 行为模式分析结果
        """
        try:
            # 获取最近90天的交互记录
            from datetime import datetime, timedelta
            ninety_days_ago = datetime.utcnow() - timedelta(days=90)
            interactions = self.db.query(UserInteractionRecord).filter(
                UserInteractionRecord.user_id == user_id,
                UserInteractionRecord.created_at >= ninety_days_ago
            ).all()
            
            if not interactions:
                return {}
            
            patterns = {
                'active_hours': defaultdict(int),  # 活跃时间段
                'preferred_weekdays': defaultdict(int),  # 偏好星期几
                'interaction_types': defaultdict(int),  # 交互类型偏好
                'avg_session_duration': 0,  # 平均会话时长
                'content_length_preference': 'medium',  # 内容长度偏好
                'topic_preferences': defaultdict(float),  # 话题偏好
                'content_type_preferences': defaultdict(float),  # 内容类型偏好
                'context_preferences': defaultdict(float),  # 上下文偏好
            }
            
            # 分析活跃时间段
            for interaction in interactions:
                created_at = getattr(interaction, 'created_at')
                interaction_type = getattr(interaction, 'interaction_type')
                
                hour = created_at.hour
                patterns['active_hours'][hour] += 1
                
                weekday = created_at.weekday()
                patterns['preferred_weekdays'][weekday] += 1
                
                patterns['interaction_types'][interaction_type] += 1
            
            # 分析话题和内容类型偏好
            for interaction in interactions:
                interaction_metadata = getattr(interaction, 'interaction_metadata')
                if interaction_metadata:
                    # 提取话题偏好
                    topics = interaction_metadata.get('topics', [])
                    if isinstance(topics, str):
                        topics = [topics]
                    for topic in topics:
                        patterns['topic_preferences'][topic] += 1
                    
                    # 提取内容类型偏好
                    content_type = interaction_metadata.get('content_type')
                    if content_type:
                        patterns['content_type_preferences'][content_type] += 1
                    
                    # 提取上下文偏好
                    context = interaction_metadata.get('context', {})
                    if context:
                        for ctx_key, ctx_value in context.items():
                            patterns['context_preferences'][f"{ctx_key}:{ctx_value}"] += 1
            
            # 确定内容长度偏好
            short_content_count = 0
            medium_content_count = 0
            long_content_count = 0
            
            for interaction in interactions:
                interaction_metadata = getattr(interaction, 'interaction_metadata')
                if interaction_metadata:
                    content_length = len(interaction_metadata.get('content', ''))
                    if content_length < 200:
                        short_content_count += 1
                    elif content_length < 500:
                        medium_content_count += 1
                    else:
                        long_content_count += 1
            
            if short_content_count > medium_content_count and short_content_count > long_content_count:
                patterns['content_length_preference'] = 'short'
            elif long_content_count > short_content_count and long_content_count > medium_content_count:
                patterns['content_length_preference'] = 'long'
            else:
                patterns['content_length_preference'] = 'medium'
            
            # 更新用户偏好
            self.update_preference(user_id, 'content_length_preference', patterns['content_length_preference'], 1.0)
            
            return patterns
            
        except Exception as e:
            logger.error(f"分析用户行为模式失败: {e}")
            return {}

    def build_user_profile(self, user_id: int) -> Dict[str, Any]:
        """
        构建完整的用户画像
        
        Args:
            user_id: 用户ID
            
        Returns:
            Dict[str, Any]: 用户画像数据
        """
        try:
            # 获取用户基本信息
            from src.models.user import User
            user = self.db.query(User).filter(User.id == user_id).first()
            if not user:
                return {}
            
            # 获取用户偏好
            preferences = self.get_user_preferences(user_id)
            
            # 分析用户行为模式
            behavior_patterns = self.analyze_user_behavior_patterns(user_id)
            
            # 构建用户画像
            profile = {
                'user_id': user_id,
                'username': user.username,
                'email': user.email,
                'role': user.role.value,
                'status': user.status.value,
                'preferences': preferences,
                'behavior_patterns': behavior_patterns,
                'interests': list(behavior_patterns.get('topic_preferences', {}).keys()),
                'preferred_content_types': list(behavior_patterns.get('content_type_preferences', {}).keys()),
                'active_hours': dict(behavior_patterns.get('active_hours', {})),
                'preferred_weekdays': dict(behavior_patterns.get('preferred_weekdays', {})),
                'interaction_history_summary': {
                    'total_interactions': sum(behavior_patterns.get('interaction_types', {}).values()),
                    'interaction_types': dict(behavior_patterns.get('interaction_types', {}))
                }
            }
            
            return profile
            
        except Exception as e:
            logger.error(f"构建用户画像失败: {e}")
            return {}


# 全局实例
user_preference_service = UserPreferenceService()


def get_user_preference_service() -> UserPreferenceService:
    """获取用户偏好服务实例"""
    return user_preference_service


class ContextMemoryService:
    """上下文记忆服务类"""
    
    def __init__(self):
        self.db_gen = get_db()
        self.db = next(self.db_gen)
    
    def save_context(self, user_id: int, session_id: str, context_data: Dict[str, Any]) -> bool:
        """
        保存上下文信息
        
        Args:
            user_id: 用户ID
            session_id: 会话ID
            context_data: 上下文数据
            
        Returns:
            bool: 是否保存成功
        """
        try:
            # 创建或更新上下文记录
            context_record = ContextRecord(
                user_id=user_id,
                session_id=session_id,
                context_data=context_data,
                created_at=datetime.utcnow(),
                updated_at=datetime.utcnow()
            )
            
            self.db.add(context_record)
            self.db.commit()
            logger.debug(f"保存上下文信息: user_id={user_id}, session_id={session_id}")
            return True
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"保存上下文信息失败: {e}")
            return False
    
    def get_context(self, user_id: int, session_id: str) -> Optional[Dict[str, Any]]:
        """
        获取上下文信息
        
        Args:
            user_id: 用户ID
            session_id: 会话ID
            
        Returns:
            Optional[Dict[str, Any]]: 上下文数据
        """
        try:
            context_record = self.db.query(ContextRecord).filter(
                ContextRecord.user_id == user_id,
                ContextRecord.session_id == session_id
            ).order_by(ContextRecord.updated_at.desc()).first()
            
            if context_record:
                context_data = getattr(context_record, 'context_data')
                return context_data
            
            return None
            
        except Exception as e:
            logger.error(f"获取上下文信息失败: {e}")
            return None
    
    def update_context(self, user_id: int, session_id: str, 
                      new_context: Dict[str, Any]) -> bool:
        """
        更新上下文信息
        
        Args:
            user_id: 用户ID
            session_id: 会话ID
            new_context: 新的上下文数据
            
        Returns:
            bool: 是否更新成功
        """
        try:
            # 获取现有上下文
            existing_context = self.get_context(user_id, session_id) or {}
            
            # 合并上下文
            merged_context = {**existing_context, **new_context}
            
            # 保存更新后的上下文
            return self.save_context(user_id, session_id, merged_context)
            
        except Exception as e:
            logger.error(f"更新上下文信息失败: {e}")
            return False


def get_context_memory_service() -> ContextMemoryService:
    """获取上下文记忆服务实例"""
    return ContextMemoryService()
